/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.catalina.mbeans;

import java.io.File;
import java.util.Vector;

import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.RuntimeOperationsException;

import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Server;
import org.apache.catalina.ServerFactory;
import org.apache.catalina.Service;
import org.apache.catalina.Valve;
import org.apache.catalina.authenticator.SingleSignOn;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardService;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.realm.DataSourceRealm;
import org.apache.catalina.realm.JDBCRealm;
import org.apache.catalina.realm.JNDIRealm;
import org.apache.catalina.realm.MemoryRealm;
import org.apache.catalina.realm.UserDatabaseRealm;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.HostConfig;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteAddrValve;
import org.apache.catalina.valves.RemoteHostValve;
import org.apache.catalina.valves.RequestDumperValve;
import org.apache.catalina.valves.ValveBase;
import org.apache.tomcat.util.modeler.BaseModelMBean;
import org.apache.tomcat.util.modeler.Registry;

/**
 * <p>
 * A <strong>ModelMBean</strong> implementation for the
 * <code>org.apache.catalina.core.StandardServer</code> component.
 * </p>
 * 
 * @author Amy Roh
 * @version $Id: MBeanFactory.java 939518 2010-04-30 00:08:58Z kkolinko $
 */

public class MBeanFactory extends BaseModelMBean {

	private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
			.getLog(MBeanFactory.class);

	/**
	 * The <code>MBeanServer</code> for this application.
	 */
	private static MBeanServer mserver = MBeanUtils.createServer();

	/**
	 * The configuration information registry for our managed beans.
	 */
	private static Registry registry = MBeanUtils.createRegistry();

	// ----------------------------------------------------------- Constructors

	/**
	 * Construct a <code>ModelMBean</code> with default
	 * <code>ModelMBeanInfo</code> information.
	 * 
	 * @exception MBeanException
	 *                if the initializer of an object throws an exception
	 * @exception RuntimeOperationsException
	 *                if an IllegalArgumentException occurs
	 */
	public MBeanFactory() throws MBeanException, RuntimeOperationsException {

		super();

	}

	// ------------------------------------------------------------- Attributes

	// ------------------------------------------------------------- Operations

	/**
	 * Return the managed bean definition for the specified bean type
	 * 
	 * @param type
	 *            MBean type
	 */
	public String findObjectName(String type) {

		if (type.equals("org.apache.catalina.core.StandardContext")) {
			return "StandardContext";
		} else if (type.equals("org.apache.catalina.core.StandardEngine")) {
			return "Engine";
		} else if (type.equals("org.apache.catalina.core.StandardHost")) {
			return "Host";
		} else {
			return null;
		}

	}

	/**
	 * Little convenience method to remove redundant code when retrieving the
	 * path string
	 * 
	 * @param t
	 *            path string
	 * @return empty string if t==null || t.equals("/")
	 */
	private final String getPathStr(String t) {
		if (t == null || t.equals("/")) {
			return "";
		}
		return t;
	}

	/**
	 * Get Parent ContainerBase to add its child component from parent's
	 * ObjectName
	 */
	private ContainerBase getParentContainerFromParent(ObjectName pname)
			throws Exception {

		String type = pname.getKeyProperty("type");
		String j2eeType = pname.getKeyProperty("j2eeType");
		Service service = getService(pname);
		StandardEngine engine = (StandardEngine) service.getContainer();
		if ((j2eeType != null) && (j2eeType.equals("WebModule"))) {
			String name = pname.getKeyProperty("name");
			name = name.substring(2);
			int i = name.indexOf("/");
			String hostName = name.substring(0, i);
			String path = name.substring(i);
			Host host = (Host) engine.findChild(hostName);
			String pathStr = getPathStr(path);
			StandardContext context = (StandardContext) host.findChild(pathStr);
			return context;
		} else if (type != null) {
			if (type.equals("Engine")) {
				return engine;
			} else if (type.equals("Host")) {
				String hostName = pname.getKeyProperty("host");
				StandardHost host = (StandardHost) engine.findChild(hostName);
				return host;
			}
		}
		return null;

	}

	/**
	 * Get Parent ContainerBase to add its child component from child
	 * component's ObjectName as a String
	 */
	private ContainerBase getParentContainerFromChild(ObjectName oname)
			throws Exception {

		String hostName = oname.getKeyProperty("host");
		String path = oname.getKeyProperty("path");
		Service service = getService(oname);
		StandardEngine engine = (StandardEngine) service.getContainer();
		if (hostName == null) {
			// child's container is Engine
			return engine;
		} else if (path == null) {
			// child's container is Host
			StandardHost host = (StandardHost) engine.findChild(hostName);
			return host;
		} else {
			// child's container is Context
			StandardHost host = (StandardHost) engine.findChild(hostName);
			path = getPathStr(path);
			StandardContext context = (StandardContext) host.findChild(path);
			return context;
		}
	}

	private Service getService(ObjectName oname) throws Exception {

		String domain = oname.getDomain();
		Server server = ServerFactory.getServer();
		Service[] services = server.findServices();
		StandardService service = null;
		for (int i = 0; i < services.length; i++) {
			service = (StandardService) services[i];
			if (domain.equals(service.getObjectName().getDomain())) {
				break;
			}
		}
		if (!service.getObjectName().getDomain().equals(domain)) {
			throw new Exception("Service with the domain is not found");
		}
		return service;

	}

	/**
	 * Create a new AccessLoggerValve.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createAccessLoggerValve(String parent) throws Exception {

		ObjectName pname = new ObjectName(parent);
		// Create a new AccessLogValve instance
		AccessLogValve accessLogger = new AccessLogValve();
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.addValve(accessLogger);
		ObjectName oname = accessLogger.getObjectName();
		return (oname.toString());

	}

	/**
	 * Create a new AjpConnector
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param address
	 *            The IP address on which to bind
	 * @param port
	 *            TCP port number to listen on
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createAjpConnector(String parent, String address, int port)
			throws Exception {

		return createConnector(parent, address, port, true, false);
	}

	/**
	 * Create a new DataSource Realm.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createDataSourceRealm(String parent, String dataSourceName,
			String roleNameCol, String userCredCol, String userNameCol,
			String userRoleTable, String userTable) throws Exception {

		// Create a new DataSourceRealm instance
		DataSourceRealm realm = new DataSourceRealm();
		realm.setDataSourceName(dataSourceName);
		realm.setRoleNameCol(roleNameCol);
		realm.setUserCredCol(userCredCol);
		realm.setUserNameCol(userNameCol);
		realm.setUserRoleTable(userRoleTable);
		realm.setUserTable(userTable);

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.setRealm(realm);
		// Return the corresponding MBean name
		ObjectName oname = realm.getObjectName();
		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new HttpConnector
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param address
	 *            The IP address on which to bind
	 * @param port
	 *            TCP port number to listen on
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createHttpConnector(String parent, String address, int port)
			throws Exception {
		return createConnector(parent, address, port, false, false);
	}

	/**
	 * Create a new Connector
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param address
	 *            The IP address on which to bind
	 * @param port
	 *            TCP port number to listen on
	 * @param isAjp
	 *            Create a AJP/1.3 Connector
	 * @param isSSL
	 *            Create a secure Connector
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	private String createConnector(String parent, String address, int port,
			boolean isAjp, boolean isSSL) throws Exception {
		Connector retobj = new Connector();
		if ((address != null) && (address.length() > 0)) {
			retobj.setProperty("address", address);
		}
		// Set port number
		retobj.setPort(port);
		// Set the protocol
		retobj.setProtocol(isAjp ? "AJP/1.3" : "HTTP/1.1");
		// Set SSL
		retobj.setSecure(isSSL);
		retobj.setScheme(isSSL ? "https" : "http");
		// Add the new instance to its parent component
		// FIX ME - addConnector will fail
		ObjectName pname = new ObjectName(parent);
		Service service = getService(pname);
		service.addConnector(retobj);

		// Return the corresponding MBean name
		ObjectName coname = retobj.getObjectName();

		return (coname.toString());
	}

	/**
	 * Create a new HttpsConnector
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param address
	 *            The IP address on which to bind
	 * @param port
	 *            TCP port number to listen on
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createHttpsConnector(String parent, String address, int port)
			throws Exception {
		return createConnector(parent, address, port, false, true);
	}

	/**
	 * Create a new JDBC Realm.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createJDBCRealm(String parent, String driverName,
			String connectionName, String connectionPassword,
			String connectionURL) throws Exception {

		// Create a new JDBCRealm instance
		JDBCRealm realm = new JDBCRealm();
		realm.setDriverName(driverName);
		realm.setConnectionName(connectionName);
		realm.setConnectionPassword(connectionPassword);
		realm.setConnectionURL(connectionURL);

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.setRealm(realm);
		// Return the corresponding MBean name
		ObjectName oname = realm.getObjectName();

		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new JNDI Realm.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createJNDIRealm(String parent) throws Exception {

		// Create a new JNDIRealm instance
		JNDIRealm realm = new JNDIRealm();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.setRealm(realm);
		// Return the corresponding MBean name
		ObjectName oname = realm.getObjectName();

		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new Memory Realm.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createMemoryRealm(String parent) throws Exception {

		// Create a new MemoryRealm instance
		MemoryRealm realm = new MemoryRealm();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.setRealm(realm);
		// Return the corresponding MBean name
		ObjectName oname = realm.getObjectName();
		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new Remote Address Filter Valve.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createRemoteAddrValve(String parent) throws Exception {

		// Create a new RemoteAddrValve instance
		RemoteAddrValve valve = new RemoteAddrValve();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		containerBase.addValve(valve);
		ObjectName oname = valve.getObjectName();
		return (oname.toString());

	}

	/**
	 * Create a new Remote Host Filter Valve.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createRemoteHostValve(String parent) throws Exception {

		// Create a new RemoteHostValve instance
		RemoteHostValve valve = new RemoteHostValve();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		containerBase.addValve(valve);
		ObjectName oname = valve.getObjectName();
		return (oname.toString());

	}

	/**
	 * Create a new Request Dumper Valve.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createRequestDumperValve(String parent) throws Exception {

		// Create a new RequestDumperValve instance
		RequestDumperValve valve = new RequestDumperValve();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		containerBase.addValve(valve);
		ObjectName oname = valve.getObjectName();
		return (oname.toString());

	}

	/**
	 * Create a new Single Sign On Valve.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createSingleSignOn(String parent) throws Exception {

		// Create a new SingleSignOn instance
		SingleSignOn valve = new SingleSignOn();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		containerBase.addValve(valve);
		ObjectName oname = valve.getObjectName();
		return (oname.toString());

	}

	/**
	 * Create a new StandardContext.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param path
	 *            The context path for this Context
	 * @param docBase
	 *            Document base directory (or WAR) for this Context
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createStandardContext(String parent, String path,
			String docBase) throws Exception {

		// XXX for backward compatibility. Remove it once supported by the admin
		return createStandardContext(parent, path, docBase, false, false,
				false, false);
	}

	/**
	 * Given a context path, get the config file name.
	 */
	private String getConfigFile(String path) {
		String basename = null;
		if (path.equals("")) {
			basename = "ROOT";
		} else {
			basename = path.substring(1).replace('/', '#');
		}
		return (basename);
	}

	/**
	 * Create a new StandardContext.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param path
	 *            The context path for this Context
	 * @param docBase
	 *            Document base directory (or WAR) for this Context
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createStandardContext(String parent, String path,
			String docBase, boolean xmlValidation, boolean xmlNamespaceAware,
			boolean tldValidation, boolean tldNamespaceAware) throws Exception {

		// Create a new StandardContext instance
		StandardContext context = new StandardContext();
		path = getPathStr(path);
		context.setPath(path);
		context.setDocBase(docBase);
		context.setXmlValidation(xmlValidation);
		context.setXmlNamespaceAware(xmlNamespaceAware);
		context.setTldValidation(tldValidation);
		context.setTldNamespaceAware(tldNamespaceAware);

		ContextConfig contextConfig = new ContextConfig();
		context.addLifecycleListener(contextConfig);

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ObjectName deployer = new ObjectName(pname.getDomain()
				+ ":type=Deployer,host=" + pname.getKeyProperty("host"));
		if (mserver.isRegistered(deployer)) {
			String contextPath = context.getPath();
			mserver.invoke(deployer, "addServiced",
					new Object[] { contextPath },
					new String[] { "java.lang.String" });
			String configPath = (String) mserver.getAttribute(deployer,
					"configBaseName");
			String baseName = getConfigFile(contextPath);
			File configFile = new File(new File(configPath), baseName + ".xml");
			context.setConfigFile(configFile.getAbsolutePath());
			mserver.invoke(deployer, "manageApp", new Object[] { context },
					new String[] { "org.apache.catalina.Context" });
			mserver.invoke(deployer, "removeServiced",
					new Object[] { contextPath },
					new String[] { "java.lang.String" });
		} else {
			log.warn("Deployer not found for " + pname.getKeyProperty("host"));
			Service service = getService(pname);
			Engine engine = (Engine) service.getContainer();
			Host host = (Host) engine.findChild(pname.getKeyProperty("host"));
			host.addChild(context);
		}

		// Return the corresponding MBean name
		ObjectName oname = context.getJmxName();

		return (oname.toString());

	}

	/**
	 * Create a new StandardEngine.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param engineName
	 *            Unique name of this Engine
	 * @param defaultHost
	 *            Default hostname of this Engine
	 * @param serviceName
	 *            Unique name of this Service
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */

	public Vector createStandardEngineService(String parent, String engineName,
			String defaultHost, String serviceName) throws Exception {

		// Create a new StandardService instance
		StandardService service = new StandardService();
		service.setName(serviceName);
		// Create a new StandardEngine instance
		StandardEngine engine = new StandardEngine();
		engine.setName(engineName);
		engine.setDefaultHost(defaultHost);
		// Need to set engine before adding it to server in order to set domain
		service.setContainer(engine);
		// Add the new instance to its parent component
		Server server = ServerFactory.getServer();
		server.addService(service);
		Vector onames = new Vector();
		// FIXME service & engine.getObjectName
		// ObjectName oname = engine.getObjectName();
		ObjectName oname = MBeanUtils.createObjectName(engineName, engine);
		onames.add(0, oname);
		// oname = service.getObjectName();
		oname = MBeanUtils.createObjectName(engineName, service);
		onames.add(1, oname);
		return (onames);

	}

	/**
	 * Create a new StandardHost.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param name
	 *            Unique name of this Host
	 * @param appBase
	 *            Application base directory name
	 * @param autoDeploy
	 *            Should we auto deploy?
	 * @param deployOnStartup
	 *            Deploy on server startup?
	 * @param deployXML
	 *            Should we deploy Context XML config files property?
	 * @param unpackWARs
	 *            Should we unpack WARs when auto deploying?
	 * @param xmlNamespaceAware
	 *            Should we turn on/off XML namespace awareness?
	 * @param xmlValidation
	 *            Should we turn on/off XML validation?
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createStandardHost(String parent, String name,
			String appBase, boolean autoDeploy, boolean deployOnStartup,
			boolean deployXML, boolean unpackWARs, boolean xmlNamespaceAware,
			boolean xmlValidation) throws Exception {

		// Create a new StandardHost instance
		StandardHost host = new StandardHost();
		host.setName(name);
		host.setAppBase(appBase);
		host.setAutoDeploy(autoDeploy);
		host.setDeployOnStartup(deployOnStartup);
		host.setDeployXML(deployXML);
		host.setUnpackWARs(unpackWARs);
		host.setXmlNamespaceAware(xmlNamespaceAware);
		host.setXmlValidation(xmlValidation);

		// add HostConfig for active reloading
		HostConfig hostConfig = new HostConfig();
		host.addLifecycleListener(hostConfig);

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		Service service = getService(pname);
		Engine engine = (Engine) service.getContainer();
		engine.addChild(host);

		// Return the corresponding MBean name
		return (host.getObjectName().toString());

	}

	/**
	 * Create a new StandardManager.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createStandardManager(String parent) throws Exception {

		// Create a new StandardManager instance
		StandardManager manager = new StandardManager();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		if (containerBase != null) {
			containerBase.setManager(manager);
		}
		ObjectName oname = manager.getObjectName();
		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new StandardService.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param name
	 *            Unique name of this StandardService
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createStandardService(String parent, String name,
			String domain) throws Exception {

		// Create a new StandardService instance
		StandardService service = new StandardService();
		service.setName(name);

		// Add the new instance to its parent component
		Server server = ServerFactory.getServer();
		server.addService(service);

		// Return the corresponding MBean name
		return (service.getObjectName().toString());

	}

	/**
	 * Create a new UserDatabaseRealm.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * @param resourceName
	 *            Global JNDI resource name of the associated UserDatabase
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createUserDatabaseRealm(String parent, String resourceName)
			throws Exception {

		// Create a new UserDatabaseRealm instance
		UserDatabaseRealm realm = new UserDatabaseRealm();
		realm.setResourceName(resourceName);

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		// Add the new instance to its parent component
		containerBase.setRealm(realm);
		// Return the corresponding MBean name
		ObjectName oname = realm.getObjectName();
		// FIXME getObjectName() returns null
		// ObjectName oname =
		// MBeanUtils.createObjectName(pname.getDomain(), realm);
		if (oname != null) {
			return (oname.toString());
		} else {
			return null;
		}

	}

	/**
	 * Create a new Web Application Loader.
	 * 
	 * @param parent
	 *            MBean Name of the associated parent component
	 * 
	 * @exception Exception
	 *                if an MBean cannot be created or registered
	 */
	public String createWebappLoader(String parent) throws Exception {

		// Create a new WebappLoader instance
		WebappLoader loader = new WebappLoader();

		// Add the new instance to its parent component
		ObjectName pname = new ObjectName(parent);
		ContainerBase containerBase = getParentContainerFromParent(pname);
		if (containerBase != null) {
			containerBase.setLoader(loader);
		}
		// FIXME add Loader.getObjectName
		// ObjectName oname = loader.getObjectName();
		ObjectName oname = MBeanUtils.createObjectName(pname.getDomain(),
				loader);
		return (oname.toString());

	}

	/**
	 * Remove an existing Connector.
	 * 
	 * @param name
	 *            MBean Name of the component to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeConnector(String name) throws Exception {

		// Acquire a reference to the component to be removed
		ObjectName oname = new ObjectName(name);
		Server server = ServerFactory.getServer();
		Service service = getService(oname);
		String port = oname.getKeyProperty("port");
		// String address = oname.getKeyProperty("address");

		Connector conns[] = (Connector[]) service.findConnectors();

		for (int i = 0; i < conns.length; i++) {
			String connAddress = String
					.valueOf(conns[i].getProperty("address"));
			String connPort = "" + conns[i].getPort();

			// if (((address.equals("null")) &&
			if ((connAddress == null) && port.equals(connPort)) {
				service.removeConnector(conns[i]);
				conns[i].destroy();
				break;
			}
			// } else if (address.equals(connAddress))
			if (port.equals(connPort)) {
				// Remove this component from its parent component
				service.removeConnector(conns[i]);
				conns[i].destroy();
				break;
			}
		}

	}

	/**
	 * Remove an existing Context.
	 * 
	 * @param contextName
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeContext(String contextName) throws Exception {

		// Acquire a reference to the component to be removed
		ObjectName oname = new ObjectName(contextName);
		String domain = oname.getDomain();
		StandardService service = (StandardService) getService(oname);

		Engine engine = (Engine) service.getContainer();
		String name = oname.getKeyProperty("name");
		name = name.substring(2);
		int i = name.indexOf("/");
		String hostName = name.substring(0, i);
		String path = name.substring(i);
		ObjectName deployer = new ObjectName(domain + ":type=Deployer,host="
				+ hostName);
		String pathStr = getPathStr(path);
		if (mserver.isRegistered(deployer)) {
			mserver.invoke(deployer, "addServiced", new Object[] { pathStr },
					new String[] { "java.lang.String" });
			mserver.invoke(deployer, "unmanageApp", new Object[] { pathStr },
					new String[] { "java.lang.String" });
			mserver.invoke(deployer, "removeServiced",
					new Object[] { pathStr },
					new String[] { "java.lang.String" });
		} else {
			log.warn("Deployer not found for " + hostName);
			Host host = (Host) engine.findChild(hostName);
			Context context = (Context) host.findChild(pathStr);
			// Remove this component from its parent component
			host.removeChild(context);
			if (context instanceof StandardContext)
				try {
					((StandardContext) context).destroy();
				} catch (Exception e) {
					log.warn("Error during context [" + context.getName()
							+ "] destroy ", e);
				}

		}

	}

	/**
	 * Remove an existing Host.
	 * 
	 * @param name
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeHost(String name) throws Exception {

		// Acquire a reference to the component to be removed
		ObjectName oname = new ObjectName(name);
		String hostName = oname.getKeyProperty("host");
		Service service = getService(oname);
		Engine engine = (Engine) service.getContainer();
		Host host = (Host) engine.findChild(hostName);

		// Remove this component from its parent component
		if (host != null) {
			if (host instanceof StandardHost)
				((StandardHost) host).destroy();
			else
				engine.removeChild(host);
		}

	}

	/**
	 * Remove an existing Loader.
	 * 
	 * @param name
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeLoader(String name) throws Exception {

		ObjectName oname = new ObjectName(name);
		// Acquire a reference to the component to be removed
		ContainerBase container = getParentContainerFromChild(oname);
		container.setLoader(null);

	}

	/**
	 * Remove an existing Manager.
	 * 
	 * @param name
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeManager(String name) throws Exception {

		ObjectName oname = new ObjectName(name);
		// Acquire a reference to the component to be removed
		ContainerBase container = getParentContainerFromChild(oname);
		container.setManager(null);

	}

	/**
	 * Remove an existing Realm.
	 * 
	 * @param name
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeRealm(String name) throws Exception {

		ObjectName oname = new ObjectName(name);
		// Acquire a reference to the component to be removed
		ContainerBase container = getParentContainerFromChild(oname);
		container.setRealm(null);
	}

	/**
	 * Remove an existing Service.
	 * 
	 * @param name
	 *            MBean Name of the component to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeService(String name) throws Exception {

		// Acquire a reference to the component to be removed
		ObjectName oname = new ObjectName(name);
		String serviceName = oname.getKeyProperty("serviceName");
		Server server = ServerFactory.getServer();
		Service service = server.findService(serviceName);

		// Remove this component from its parent component
		server.removeService(service);

	}

	/**
	 * Remove an existing Valve.
	 * 
	 * @param name
	 *            MBean Name of the comonent to remove
	 * 
	 * @exception Exception
	 *                if a component cannot be removed
	 */
	public void removeValve(String name) throws Exception {

		// Acquire a reference to the component to be removed
		ObjectName oname = new ObjectName(name);
		ContainerBase container = getParentContainerFromChild(oname);
		String sequence = oname.getKeyProperty("seq");
		Valve[] valves = (Valve[]) container.getValves();
		for (int i = 0; i < valves.length; i++) {
			ObjectName voname = ((ValveBase) valves[i]).getObjectName();
			if (voname.equals(oname)) {
				container.removeValve(valves[i]);
			}
		}
	}

}
